#!/usr/perl5/bin/perl
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#

#
# This program manages the "active" print service selection.
#   If called as 'print-service', it takes one of four options.
#   Options:
#     [-s[et] service [-m]]	Select the "active" print service, optionally
#				migrating basic print queue configuration.
#     [-q[uery]]		Display the "active" print service.
#     [-e[xport] file]		Export basic print queue configuration to
#				a file.
#     [-i[mport] file]		Import basic print queue configuration from
#				a file.
#
#   If called by any other name, it will look for a corresponding command
#   under /usr/lib/{active-service}/bin/{command} and execute that program
#   with the original arguments.
#

use Getopt::Long;
use File::Basename;
use File::Copy;
use File::Temp qw/ :POSIX /;

my $cmd = basename($0);

my $LPSTAT = '/usr/bin/lpstat';
my $LPADMIN = '/usr/sbin/lpadmin';
my $ENABLE = '/usr/bin/enable';
my $ACCEPT = '/usr/sbin/accept';
my $SVCADM = '/usr/sbin/svcadm';
my $SVCPROP = '/usr/bin/svcprop';
my $SVCCFG = '/usr/sbin/svccfg';
my $SVC_LP_SCHEDULER = 'print/server';
my $SVC_LP_LPD = 'print/rfc1179';
my $SVC_LP_IPP = 'print/ipp-listener';
my $SVC_LP_PPD = 'print/ppd-cache-update';
my $SVC_CUPS_SCHEDULER = 'cups/scheduler';
my $SVC_CUPS_LPD = 'cups/in-lpd';

sub fatal {
	($ENV{"DESKTOP_LAUNCHED"}) &&
		exec("/bin/zenity", "--error", "--text=@_");
	print STDERR @_;
	exit(1);
}

sub usage {
	print STDERR <<EOF ;
Usage:
  $cmd [-s[et] service [-m]]	Select the \"active\" print service,
					optionally migrating basic print queue
					configuration.
  $cmd [-q[uery]]		Display the "active" print service.
  $cmd [-e[xport] file]	Export basic print queue configuration
					to a file.
  $cmd [-i[mport] file]	Import basic print queue configuration
					from a file.
EOF
	exit(1);
}

sub svcprop {
	local ($fmri, $property) = @_;
	my $FH;

	open($FH, "$SVCPROP -C -p $property $fmri 2>/dev/null |");
	$result = <$FH>;
	close($FH);

	return ($result);
}

sub svccfg {
	local ($fmri, $operation) = @_;
	my $FH;

	open($FH, "$SVCCFG -s $fmri \"$operation\" 2>/dev/null |");
	$result = <$FH>;
	close($FH);

	return ($result);
}

sub svcadm {
	local ($operation, @fmris) = @_;

	system("$SVCADM $operation -s @fmris");
}


sub print_service {
	my $service;

	$service = svcprop("$SVC_CUPS_SCHEDULER:default", "general/active");
	($service =~ /true/) && ($service = 'cups') || ($service = 'lp');

	return ($service);
}

sub print_command {
	local($command, @av) = @_;
	my $service = print_service();

	if (!defined($service)) {
		fatal("failed to detect active print service: $!\n");
	}

	if (! -d "/usr/lib/$service/bin") {
		fatal("print service: $service is not installed\n");
	}

	my $executable = "/usr/lib/$service/bin/$command";
	# CUPS has it's own names for enable and disable
	($command =~ /(en|dis)able/) && ($service eq 'cups') &&
		(! -x $executable) &&
		($executable = "/usr/lib/$service/bin/$service$command");

	if (! -x $executable) {
		fatal("$command is not available from $service print service\n");
	}

	exec($executable, @ARGV);
}

sub export_print_queues {
	local ($path) = @_;
	my $service = print_service();

	if ($service eq 'lp') {
		my $FH, $DFH;

		open($FH, ">$path");
		open($DFH, "$LPSTAT -v|");
		while (<$DFH>) {
			if (/device for (.+): (.+)/) {
				my $EFH;

				print $FH "<Printer $1>\nDeviceURI $2\n";
				open($EFH, "$LPSTAT -p $1 -l |");
				while (<$EFH>) {
					(/Description: (.+)/) &&
						print $FH "Info $1\n";
				}
				close($EFH);
				print $FH "</Printer>\n";
			}
		}
		close($DFH);
		close($FH);
	} else {
		copy('/etc/cups/printers.conf', $path);
	}
}

sub psystem {
	print "  @_\n";
	system(@_);
}

sub import_print_queues {
	local ($path) = @_;
	my $service = print_service();
	my $FH, %printer, @options;

	# store queue info in the 'active' print service
	open($FH, "<$path");
	while (<$FH>) {
		if (/<Printer (.+)>/) {
			$printer{'Printer'} = $1;
			@options = ();
			push(@options, "-p", $1);
		} elsif (/([^\s]+)\s(.+)/) {
			$printer{$1} = $2;
			my $value = $2;
			($1 eq 'DeviceURI') &&
				push(@options, "-v", $value);
			($1 eq 'Info') && 
				push(@options, "-D", $value);
		} elsif (/<\/Printer>/) {
			($service eq 'lp') &&
				push(@options, "-m", "uri");
			print "importing $printer{'Printer'}...\n";
			# create a queue
			psystem($LPADMIN, @options);
			psystem($ENABLE, $printer{'Printer'});
			($printer{'Accepting'} eq 'Yes') &&
				psystem($ACCEPT, $printer{'Printer'});
			$printer = ();
		}
	}
	close($FH);
}

sub select_service {
	my ($service, $migrate) = @_;
	my $FH, $queues;

	if (! -d "/usr/lib/$service/bin") {
		fatal("print service: $service is not installed\n");
	}

	if ($migrate == 1) {
		# export old print queue configuration (if migrating)
		$queues = tmpnam();
		export_print_queues($queues);
	}

	# enable/disable the services
	if ($service eq 'cups') {
		(-f '/etc/printers.conf') && (! -f '/etc/lp/printers.conf') &&
			rename('/etc/printers.conf', '/etc/lp/printers.conf');
		print("disabling LP services...\n");
		svcadm("disable", $SVC_LP_SCHEDULER, $SVC_LP_IPP, $SVC_LP_LPD,
				  $SVC_LP_PPD);
		print("enabling CUPS services...\n");
		svcadm("enable", $SVC_CUPS_SCHEDULER, $SVC_CUPS_LPD);
		svccfg("cups/scheduler:default",
			"setprop general/active = boolean: true");
		system("pkill -USR1 -f '/desktop-print-management-applet'"); 
	} else {
		print("disabling CUPS services...\n");
		svcadm("disable", $SVC_CUPS_SCHEDULER, $SVC_CUPS_LPD);
		print("enabling LP services...\n");
		svcadm("enable", $SVC_LP_SCHEDULER, $SVC_LP_IPP, $SVC_LP_LPD,
				  $SVC_LP_PPD);
		(-f '/etc/lp/printers.conf') &&
			rename('/etc/lp/printers.conf', '/etc/printers.conf');
		svccfg("cups/scheduler:default", "delprop general/active");
		system("pkill -USR1 -f '/desktop-print-management-applet'"); 
	}

	# import the new print queue configuration (if migrating)
	defined($queues) && import_print_queues($queues);
}

sub query_service {
	my $service = print_service();

	if (!defined($service)) {
		fatal("failed to detect active print service: $!\n");
	}
	print "active print service: $service\n";
}

if ($cmd eq 'print-service') {
	my ($import_path, $export_path, $svc_name, $query, $migrate) = ();

	my $res = GetOptions('q|query' => \$query, 's|set=s' => \$service,
		'm|migrate' => \$migrate, 'e|export=s' => \$export_path,
		'i|import=s' => \$import_path);

	($res) || usage();

	if (defined($import_path) && !defined($export_path) &&
	    !defined($query) && !defined($service) && !defined($migrate)) {
		import_print_queues($import_path);
	} elsif (!defined($import_path) && defined($export_path) &&
		 !defined($query) && !defined($service) && !defined($migrate)) {
		export_print_queues($export_path);
	} elsif (!defined($import_path) && !defined($export_path) &&
		 defined($query) && !defined($service) && !defined($migrate)) {
		query_service();
	} elsif (!defined($import_path) && !defined($export_path) &&
		 !defined($query) && defined($service)) {
		select_service($service, $migrate);
	} else {
		usage();
	}
} else {
	print_command($cmd, @ARGV);
}

exit(0);
